/** @file

  Copyright (c) 2004  - 2019, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

Module Name:


  Platform.c

Abstract:

  Platform Initialization Driver.


--*/

#include "PlatformDxe.h"
#include "Platform.h"
#include "PchCommonDefinitions.h"
#include <Protocol/PchPlatformPolicy.h>
#include <Protocol/CpuIo2.h>
#include <Library/S3BootScriptLib.h>
#include <Guid/PciLanInfo.h>
#include <Guid/ItkData.h>
#include <Library/PciLib.h>
#include <PlatformBootMode.h>
#include <Guid/EventGroup.h>
#include <Guid/Vlv2Variable.h>
#include <Protocol/GlobalNvsArea.h>
#include <Protocol/IgdOpRegion.h>
#include <Library/PcdLib.h>
#include <Protocol/VariableLock.h>


//
// VLV2 GPIO GROUP OFFSET
//
#define GPIO_SCORE_OFFSET	0x0000
#define GPIO_NCORE_OFFSET	0x1000
#define GPIO_SSUS_OFFSET	0x2000

typedef struct {
  UINT32 offset;
  UINT32 val;
} CFIO_PNP_INIT;

GPIO_CONF_PAD_INIT mTB_BL_GpioInitData_SC_TRI_Exit_boot_Service[] =
{
//              Pad Name          GPIO Number     Used As   GPO Default  Function#     INT Capable   Interrupt Type   PULL H/L    MMIO Offset
  GPIO_INIT_ITEM("LPC_CLKOUT0       GPIOC_47 "     ,TRISTS   ,NA           ,F0           ,             ,                ,NONE       ,0x47),
  GPIO_INIT_ITEM("LPC_CLKOUT1       GPIOC_48 "     ,TRISTS   ,NA           ,F0           ,             ,                ,NONE       ,0x41),
};


EFI_GUID mSystemHiiExportDatabase = EFI_HII_EXPORT_DATABASE_GUID;
SYSTEM_CONFIGURATION  mSystemConfiguration;
EFI_HANDLE            mImageHandle;
BOOLEAN               mMfgMode = FALSE;
UINT32                mPlatformBootMode = PLATFORM_NORMAL_MODE;
extern CHAR16 gItkDataVarName[];


EFI_PLATFORM_INFO_HOB      mPlatformInfo;
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *mPciRootBridgeIo;
EFI_EVENT  mReadyToBootEvent;

UINT8 mSmbusRsvdAddresses[] = PLATFORM_SMBUS_RSVD_ADDRESSES;
UINT8 mNumberSmbusAddress = sizeof( mSmbusRsvdAddresses ) / sizeof( mSmbusRsvdAddresses[0] );

UINTN   mPciLanCount = 0;
VOID    *mPciLanInfo = NULL;
UINTN   SpiBase;

static EFI_SPEAKER_IF_PROTOCOL mSpeakerInterface = {
  ProgramToneFrequency,
  GenerateBeepTone
};

CFIO_PNP_INIT mTB_BL_GpioInitData_SC_TRI_S0ix_Exit_boot_Service[] =
{
  {0x410 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_clkout1_pconf0
  {0x470 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_clkout0_pconf0
  {0x560 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_ilb_serirq_pconf0
  {0x450 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_frameb_pconf0
  {0x480 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_clkrunb_pconf0
  {0x420 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad3_pconf0
  {0x430 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad2_pconf0
  {0x440 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad1_pconf0
  {0x460 ,0x20038e10},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad0_pconf0
  {0x418 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_clkout1_pad_val
  {0x478 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_clkout0_pad_val
  {0x568 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_ilb_serirq_pad_val
  {0x458 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_frameb_pad_val
  {0x488 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_clkrunb_pad_val
  {0x428 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad3_pad_val
  {0x438 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad2_pad_val
  {0x448 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad1_pad_val
  {0x468 ,0x00000006},  //vlv.gpio.gpscore.cfio_regs_pad_lpc_ad0_pad_val
};

VOID
EfiOrMem (
  IN VOID   *Destination,
  IN VOID   *Source,
  IN UINTN  Length
  );

#if defined(FIRMWARE_ID_BACKWARD_COMPATIBLE) && (FIRMWARE_ID_BACKWARD_COMPATIBLE != 0)
STATIC
VOID
InitFirmwareId();
#endif


VOID
InitializeClockRouting(
  );

VOID
InitializeSlotInfo (
  );

#if defined(SENSOR_INFO_VAR_SUPPORT) && SENSOR_INFO_VAR_SUPPORT != 0
VOID
InitializeSensorInfoVariable (
  );
#endif

VOID
InitTcoReset (
  );

VOID
InitExI ();

VOID
InitItk();

VOID
InitPlatformBootMode();

VOID
InitMfgAndConfigModeStateVar();

VOID
InitPchPlatformPolicy (
  IN EFI_PLATFORM_INFO_HOB      *PlatformInfo
  );

VOID
InitVlvPlatformPolicy (
  );

VOID
PchInitBeforeBoot(
  );

VOID
UpdateDVMTSetup(
  );

VOID
InitRC6Policy(
  VOID
  );


EFI_STATUS
EFIAPI
SaveSetupRecoveryVar(
  VOID
  )
{
  EFI_STATUS                   Status = EFI_SUCCESS;
  UINTN                        SizeOfNvStore = 0;
  UINTN                        SizeOfSetupVar = 0;
  SYSTEM_CONFIGURATION         *SetupData = NULL;
  SYSTEM_CONFIGURATION         *RecoveryNvData = NULL;
  EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock = NULL;


  DEBUG ((EFI_D_INFO, "SaveSetupRecoveryVar() Entry \n"));
  SizeOfNvStore = sizeof(SYSTEM_CONFIGURATION);
  RecoveryNvData = AllocateZeroPool (sizeof(SYSTEM_CONFIGURATION));
  if (NULL == RecoveryNvData) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Exit; 
  }
  
  Status = gRT->GetVariable(
                L"SetupRecovery",
                &gEfiNormalSetupGuid,
                NULL,
                &SizeOfNvStore,
                RecoveryNvData
                );
  
  if (EFI_ERROR (Status)) {
    // Don't find the "SetupRecovery" variable.
    // have to copy "Setup" variable to "SetupRecovery" variable.
    SetupData = AllocateZeroPool (sizeof(SYSTEM_CONFIGURATION));
    if (NULL == SetupData) {
      Status = EFI_OUT_OF_RESOURCES;
      goto Exit;      
    }
    SizeOfSetupVar = sizeof(SYSTEM_CONFIGURATION);
    Status = gRT->GetVariable(
                    NORMAL_SETUP_NAME,
                    &gEfiNormalSetupGuid,
                    NULL,
                    &SizeOfSetupVar,
                    SetupData
                    );
    ASSERT_EFI_ERROR (Status);
    
    Status = gRT->SetVariable (
                    L"SetupRecovery",
                    &gEfiNormalSetupGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                    sizeof(SYSTEM_CONFIGURATION),
                    SetupData
                    );
    ASSERT_EFI_ERROR (Status);

    Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
      if (!EFI_ERROR (Status)) {
        Status = VariableLock->RequestToLock (VariableLock, L"SetupRecovery", &gEfiNormalSetupGuid);
        ASSERT_EFI_ERROR (Status);
    }
    
  }

Exit:
  if (RecoveryNvData)
    FreePool (RecoveryNvData);
  if (SetupData)
    FreePool (SetupData);
  
  return Status;
    
}


VOID
TristateLpcGpioConfig (
  IN UINT32             Gpio_Mmio_Offset,
  IN UINT32             Gpio_Pin_Num,
  GPIO_CONF_PAD_INIT*   Gpio_Conf_Data
  )

{
  UINT32    index;
  UINT32    mmio_conf0;
  UINT32    mmio_padval;
  PAD_CONF0 conf0_val;
  PAD_VAL   pad_val;

  //
  // GPIO WELL -- Memory base registers
  //

  //
  // A0 BIOS Spec doesn't mention it although X0 does. comment out now.
  // GPIO write 0x01001002 to IOBASE + Gpio_Mmio_Offset + 0x0900
  //

  for(index=0; index < Gpio_Pin_Num; index++)
  {
    //
    // Calculate the MMIO Address for specific GPIO pin CONF0 register pointed by index.
    //
    mmio_conf0 = IO_BASE_ADDRESS + Gpio_Mmio_Offset + R_PCH_CFIO_PAD_CONF0 + Gpio_Conf_Data[index].offset * 16;
    mmio_padval= IO_BASE_ADDRESS + Gpio_Mmio_Offset + R_PCH_CFIO_PAD_VAL   + Gpio_Conf_Data[index].offset * 16;

#ifdef EFI_DEBUG
    DEBUG ((EFI_D_INFO, "%s, ", Gpio_Conf_Data[index].pad_name));

#endif
    DEBUG ((EFI_D_INFO, "Usage = %d, Func# = %d, IntType = %d, Pull Up/Down = %d, MMIO Base = 0x%08x, ",
      Gpio_Conf_Data[index].usage,
      Gpio_Conf_Data[index].func,
      Gpio_Conf_Data[index].int_type,
      Gpio_Conf_Data[index].pull,
      mmio_conf0));

    //
    // Step 1: PadVal Programming
    //
    pad_val.dw = MmioRead32(mmio_padval);

    //
    // Config PAD_VAL only for GPIO (Non-Native) Pin
    //
    if(Native != Gpio_Conf_Data[index].usage)
    {
      pad_val.dw &= ~0x6; // Clear bits 1:2
      pad_val.dw |= (Gpio_Conf_Data[index].usage & 0x6);  // Set bits 1:2 according to PadVal

        //
        // set GPO default value
        //
        if(Gpio_Conf_Data[index].usage == GPO && Gpio_Conf_Data[index].gpod4 != NA)
        {
        pad_val.r.pad_val = Gpio_Conf_Data[index].gpod4;
        }
    }


    DEBUG ((EFI_D_INFO, "Set PAD_VAL = 0x%08x, ", pad_val.dw));

    MmioWrite32(mmio_padval, pad_val.dw);

    //
    // Step 2: CONF0 Programming
    // Read GPIO default CONF0 value, which is assumed to be default value after reset.
    //
    conf0_val.dw = MmioRead32(mmio_conf0);

    //
    // Set Function #
    //
    conf0_val.r.Func_Pin_Mux = Gpio_Conf_Data[index].func;

    if(GPO == Gpio_Conf_Data[index].usage)
    {
      //
      // If used as GPO, then internal pull need to be disabled
      //
      conf0_val.r.Pull_assign = 0;  // Non-pull
    }
    else
    {
      //
      // Set PullUp / PullDown
      //
      if(P_20K_H == Gpio_Conf_Data[index].pull)
      {
        conf0_val.r.Pull_assign = 0x1;  // PullUp
        conf0_val.r.Pull_strength = 0x2;// 20K
      }
      else if(P_20K_L == Gpio_Conf_Data[index].pull)
      {
        conf0_val.r.Pull_assign = 0x2;  // PullDown
        conf0_val.r.Pull_strength = 0x2;// 20K
      }
      else if(P_NONE == Gpio_Conf_Data[index].pull)
      {
        conf0_val.r.Pull_assign = 0;	// Non-pull
      }
      else
      {
        ASSERT(FALSE);  // Invalid value
      }
    }

    //
    // Set INT Trigger Type
    //
    conf0_val.dw &= ~0x0f000000;  // Clear bits 27:24

    //
    // Set INT Trigger Type
    //
    if(TRIG_ == Gpio_Conf_Data[index].int_type)
    {
      //
      // Interrupt not capable, clear bits 27:24
      //
    }
    else
    {
      conf0_val.dw |= (Gpio_Conf_Data[index].int_type & 0x0f)<<24;
    }

    DEBUG ((EFI_D_INFO, "Set CONF0 = 0x%08x\n", conf0_val.dw));

    //
    // Write back the targeted GPIO config value according to platform (board) GPIO setting
    //
    MmioWrite32 (mmio_conf0, conf0_val.dw);
  }

  // A0 BIOS Spec doesn't mention it although X0 does. comment out now.
  // GPIO SCORE write 0x01001002 to IOBASE + 0x0900
  //
}

VOID
EFIAPI
SpiBiosProtectionFunction(
  EFI_EVENT Event,
  VOID      *Context
  )
{

  UINTN                             mPciD31F0RegBase;
  UINTN                             BiosFlaLower0;
  UINTN                             BiosFlaLimit0;
  UINTN                             BiosFlaLower1;
  UINTN                             BiosFlaLimit1;  
  

  BiosFlaLower0 = PcdGet32(PcdFlashMicroCodeAddress)-PcdGet32(PcdFlashAreaBaseAddress);
  BiosFlaLimit0 = PcdGet32(PcdFlashMicroCodeSize)-1;  
  BiosFlaLower1 = PcdGet32(PcdFlashFvMainBase)-PcdGet32(PcdFlashAreaBaseAddress);
  BiosFlaLimit1 = (PcdGet32(PcdFlashFvRecoveryBase)-PcdGet32(PcdFlashFvMainBase)+PcdGet32(PcdFlashFvRecoverySize))-1;
  
  mPciD31F0RegBase = MmPciAddress (0,
                         DEFAULT_PCI_BUS_NUMBER_PCH,
                         PCI_DEVICE_NUMBER_PCH_LPC,
                         PCI_FUNCTION_NUMBER_PCH_LPC,
                         0
                       );
  SpiBase          = MmioRead32(mPciD31F0RegBase + R_PCH_LPC_SPI_BASE) & B_PCH_LPC_SPI_BASE_BAR;

  //
  //Set SMM_BWP, WPD and LE bit
  //
  MmioOr32 ((UINTN) (SpiBase + R_PCH_SPI_BCR), (UINT8) B_PCH_SPI_BCR_SMM_BWP);
  MmioAnd32 ((UINTN) (SpiBase + R_PCH_SPI_BCR), (UINT8)(~B_PCH_SPI_BCR_BIOSWE));
  MmioOr32 ((UINTN) (SpiBase + R_PCH_SPI_BCR), (UINT8) B_PCH_SPI_BCR_BLE);

  //
  //First check if FLOCKDN or PR0FLOCKDN is set. No action if either of them set already.
  //
  if( (MmioRead16(SpiBase + R_PCH_SPI_HSFS) & B_PCH_SPI_HSFS_FLOCKDN) != 0 ||
      (MmioRead32(SpiBase + R_PCH_SPI_IND_LOCK)& B_PCH_SPI_IND_LOCK_PR0) != 0) {
    //
    //Already locked. we could take no action here
    //
    DEBUG((EFI_D_INFO, "PR0 already locked down. Stop configuring PR0.\n"));
    return;
  }

  //
  //Set PR0
  //
  MmioOr32((UINTN)(SpiBase + R_PCH_SPI_PR0),
    B_PCH_SPI_PR0_RPE|B_PCH_SPI_PR0_WPE|\
    (B_PCH_SPI_PR0_PRB_MASK&(BiosFlaLower0>>12))|(B_PCH_SPI_PR0_PRL_MASK&(BiosFlaLimit0>>12)<<16));

  //
  //Set PR1
  //

  MmioOr32((UINTN)(SpiBase + R_PCH_SPI_PR1),
    B_PCH_SPI_PR1_RPE|B_PCH_SPI_PR1_WPE|\
    (B_PCH_SPI_PR1_PRB_MASK&(BiosFlaLower1>>12))|(B_PCH_SPI_PR1_PRL_MASK&(BiosFlaLimit1>>12)<<16));

  //
  //Lock down PRx
  //
  MmioOr16 ((UINTN) (SpiBase + R_PCH_SPI_HSFS), (UINT16) (B_PCH_SPI_HSFS_FLOCKDN));

  //
  // Verify if it's really locked.
  //
  if ((MmioRead16 (SpiBase + R_PCH_SPI_HSFS) & B_PCH_SPI_HSFS_FLOCKDN) == 0) {
    DEBUG((EFI_D_ERROR, "Failed to lock down PRx.\n"));
  }
  return;

}

VOID
EFIAPI
InitPciDevPME (
  EFI_EVENT  Event,
  VOID       *Context
  )
{
  UINTN                  VarSize;

  VarSize = sizeof(SYSTEM_CONFIGURATION);
  gRT->GetVariable(
         NORMAL_SETUP_NAME,
         &gEfiNormalSetupGuid,
         NULL,
         &VarSize,
         &mSystemConfiguration
         );

  //
  //Program HDA PME_EN
  //
  PchAzaliaPciCfg32Or (R_PCH_HDA_PCS, B_PCH_HDA_PCS_PMEE);

  //
  //Program SATA PME_EN
  //
  PchSataPciCfg32Or (R_PCH_SATA_PMCS, B_PCH_SATA_PMCS_PMEE);

  DEBUG ((EFI_D_INFO, "InitPciDevPME mSystemConfiguration.EhciPllCfgEnable = 0x%x \n",mSystemConfiguration.EhciPllCfgEnable));
 if (mSystemConfiguration.EhciPllCfgEnable != 1) {
  //
  //Program EHCI PME_EN
  //
  PchMmPci32Or (
    0,
    0,
    PCI_DEVICE_NUMBER_PCH_USB,
    PCI_FUNCTION_NUMBER_PCH_EHCI,
    R_PCH_EHCI_PWR_CNTL_STS,
    B_PCH_EHCI_PWR_CNTL_STS_PME_EN
    );
 }
   {
     UINTN                 EhciPciMmBase;
     UINT32                Buffer32 = 0;

    EhciPciMmBase = MmPciAddress (0,
                      0,
                      PCI_DEVICE_NUMBER_PCH_USB,
                      PCI_FUNCTION_NUMBER_PCH_EHCI,
                      0
                    );
    DEBUG ((EFI_D_INFO, "ConfigureAdditionalPm() EhciPciMmBase = 0x%x \n",EhciPciMmBase));
    Buffer32 = MmioRead32(EhciPciMmBase + R_PCH_EHCI_PWR_CNTL_STS);
    DEBUG ((EFI_D_INFO, "ConfigureAdditionalPm() R_PCH_EHCI_PWR_CNTL_STS = 0x%x \n",Buffer32));
  }
}

VOID
EFIAPI
InitThermalZone (
  EFI_EVENT  Event,
  VOID       *Context
  )
{
  UINTN                  VarSize;
  EFI_GLOBAL_NVS_AREA_PROTOCOL       *GlobalNvsArea;
  VarSize = sizeof(SYSTEM_CONFIGURATION);
  gRT->GetVariable(
         NORMAL_SETUP_NAME,
         &gEfiNormalSetupGuid,
         NULL,
         &VarSize,
         &mSystemConfiguration
         );
  gBS->LocateProtocol (
         &gEfiGlobalNvsAreaProtocolGuid,
         NULL,
         (void **)&GlobalNvsArea
         );
  GlobalNvsArea->Area->CriticalThermalTripPoint = mSystemConfiguration.CriticalThermalTripPoint;
  GlobalNvsArea->Area->PassiveThermalTripPoint = mSystemConfiguration.PassiveThermalTripPoint;
}
#if defined SUPPORT_LVDS_DISPLAY && SUPPORT_LVDS_DISPLAY

#endif


EFI_STATUS
EFIAPI
TristateLpcGpioS0i3Config (
  UINT32             Gpio_Mmio_Offset,
  UINT32             Gpio_Pin_Num,
  CFIO_PNP_INIT*   Gpio_Conf_Data
  )
{

  UINT32	  index;
  UINT32	  mmio_reg;
  UINT32	  mmio_val;

    DEBUG ((DEBUG_INFO, "TristateLpcGpioS0i3Config\n"));

    for(index=0; index < Gpio_Pin_Num; index++)
    {
      mmio_reg = IO_BASE_ADDRESS + Gpio_Mmio_Offset + Gpio_Conf_Data[index].offset;

      MmioWrite32(mmio_reg, Gpio_Conf_Data[index].val);
      mmio_val = 0;
      mmio_val = MmioRead32(mmio_reg);

      DEBUG ((EFI_D_INFO, "Set MMIO=0x%08x  PAD_VAL = 0x%08x,\n", mmio_reg, mmio_val));
    }

     return EFI_SUCCESS;
}


/**
  Event Notification during exit boot service to enabel ACPI mode

   Disable SW SMI Timer, SMI from USB & Intel Specific USB 2

   Clear all ACPI event status and disable all ACPI events
   Disable PM sources except power button
   Clear status bits

   Guarantee day-of-month alarm is invalid (ACPI 5.0 Section 4.8.2.4 "Real Time Clock Alarm")

   Update EC to disable SMI and enable SCI

   Enable SCI

   Enable PME_B0_EN in GPE0a_EN

  @param Event  - EFI Event Handle
  @param Context - Pointer to Notify Context

  @retval  Nothing

**/
VOID
EFIAPI
EnableAcpiCallback (
  IN EFI_EVENT        Event,
  IN VOID             *Context
  )
{
  UINT32  RegData32;
  UINT16  Pm1Cnt;
  UINT16  AcpiBase;
  UINT32  Gpe0aEn;

  AcpiBase = MmioRead16 (
               PchPciDeviceMmBase (DEFAULT_PCI_BUS_NUMBER_PCH,
               PCI_DEVICE_NUMBER_PCH_LPC,
               PCI_FUNCTION_NUMBER_PCH_LPC) + R_PCH_LPC_ACPI_BASE
               ) & B_PCH_LPC_ACPI_BASE_BAR;

  DEBUG ((EFI_D_INFO, "EnableAcpiCallback: AcpiBase = %x\n", AcpiBase));

  //
  // Disable SW SMI Timer, SMI from USB & Intel Specific USB 2
  //
  RegData32 = IoRead32(AcpiBase + R_PCH_SMI_EN);
  RegData32 &= ~(B_PCH_SMI_EN_SWSMI_TMR | B_PCH_SMI_EN_LEGACY_USB2 | B_PCH_SMI_EN_INTEL_USB2);
  IoWrite32(AcpiBase + R_PCH_SMI_EN, RegData32);

  RegData32 = IoRead32(AcpiBase + R_PCH_SMI_STS);
  RegData32 |= B_PCH_SMI_STS_SWSMI_TMR;
  IoWrite32(AcpiBase + R_PCH_SMI_STS, RegData32);

  //
  // Disable PM sources except power button
  // power button is enabled only for PCAT. Disabled it on Tablet platform
  //

  IoWrite16(AcpiBase + R_PCH_ACPI_PM1_EN, B_PCH_ACPI_PM1_EN_PWRBTN);
  IoWrite16(AcpiBase + R_PCH_ACPI_PM1_STS, 0xffff);

  //
  // Guarantee day-of-month alarm is invalid (ACPI 5.0 Section 4.8.2.4 "Real Time Clock Alarm")
  // Clear Status D reg VM bit, Date of month Alarm to make Data in CMOS RAM is no longer Valid
  //
  IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, RTC_ADDRESS_REGISTER_D);
  IoWrite8 (PCAT_RTC_DATA_REGISTER, 0x0);

  RegData32 = IoRead32(AcpiBase + R_PCH_ALT_GP_SMI_EN);
  RegData32 &= ~(BIT7);
  IoWrite32((AcpiBase + R_PCH_ALT_GP_SMI_EN), RegData32);

  //
  // Enable SCI
  //
  Pm1Cnt = IoRead16(AcpiBase + R_PCH_ACPI_PM1_CNT);
  Pm1Cnt |= B_PCH_ACPI_PM1_CNT_SCI_EN;
  IoWrite16(AcpiBase + R_PCH_ACPI_PM1_CNT, Pm1Cnt);

  IoWrite8(0x80, 0xA0);	//SW_SMI_ACPI_ENABLE

  //
  // Enable PME_B0_EN in GPE0a_EN
  // Caution: Enable PME_B0_EN must be placed after enabling SCI.
  // Otherwise, USB PME could not be handled as SMI event since no handler is there.
  //
  Gpe0aEn = IoRead32 (AcpiBase + R_PCH_ACPI_GPE0a_EN);
  Gpe0aEn |= B_PCH_ACPI_GPE0a_EN_PME_B0;
  IoWrite32(AcpiBase + R_PCH_ACPI_GPE0a_EN, Gpe0aEn);

}

/**

  Routine Description:

  This is the standard EFI driver point for the Driver. This
  driver is responsible for setting up any platform specific policy or
  initialization information.

  @param ImageHandle    Handle for the image of this driver.
  @param SystemTable    Pointer to the EFI System Table.

  @retval EFI_SUCCESS   Policy decisions set.

**/
EFI_STATUS
EFIAPI
InitializePlatform (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_STATUS                          Status;
  UINTN                               VarSize;
  EFI_HANDLE                          Handle = NULL;
  EFI_EVENT                           mEfiExitBootServicesEvent;
  EFI_EVENT                           RtcEvent;
  VOID                                *RtcCallbackReg = NULL;
  
  mImageHandle = ImageHandle;

  Status = gBS->InstallProtocolInterface (
                  &Handle,
                  &gEfiSpeakerInterfaceProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mSpeakerInterface
                  );

  Status = gBS->LocateProtocol (
                  &gEfiPciRootBridgeIoProtocolGuid,
                  NULL,
                  (VOID **) &mPciRootBridgeIo
                  );
  ASSERT_EFI_ERROR (Status);

  VarSize = sizeof(EFI_PLATFORM_INFO_HOB);
  Status = gRT->GetVariable(
                  L"PlatformInfo",
                  &gEfiVlv2VariableGuid,
                  NULL,
                  &VarSize,
                  &mPlatformInfo
                  );

  //
  // Initialize Product Board ID variable
  //
  InitMfgAndConfigModeStateVar();
  InitPlatformBootMode();

  //
  // Install Observable protocol
  //
  InitializeObservableProtocol();

  Status = SaveSetupRecoveryVar();
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "InitializePlatform() Save SetupRecovery variable failed \n"));
  }

  VarSize = sizeof(SYSTEM_CONFIGURATION);
  Status = gRT->GetVariable(
                  NORMAL_SETUP_NAME,
                  &gEfiNormalSetupGuid,
                  NULL,
                  &VarSize,
                  &mSystemConfiguration
                  );
  if (EFI_ERROR (Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
    //The setup variable is corrupted
    VarSize = sizeof(SYSTEM_CONFIGURATION);
    Status = gRT->GetVariable(
              L"SetupRecovery",
              &gEfiNormalSetupGuid,
              NULL,
              &VarSize,
              &mSystemConfiguration
              );
    ASSERT_EFI_ERROR (Status);
    Status = gRT->SetVariable (
                    NORMAL_SETUP_NAME,
                    &gEfiNormalSetupGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                    sizeof(SYSTEM_CONFIGURATION),
                    &mSystemConfiguration
                    );    
  }
    
  Status = EfiCreateEventReadyToBootEx (
             TPL_CALLBACK,
             ReadyToBootFunction,
             NULL,
             &mReadyToBootEvent
             );

  //
  // Create a ReadyToBoot Event to run the PME init process
  //
  Status = EfiCreateEventReadyToBootEx (
             TPL_CALLBACK,
             InitPciDevPME,
             NULL,
             &mReadyToBootEvent
             );
  //
  // Create a ReadyToBoot Event to run enable PR0/PR1 and lock down,unlock variable region
  //
  if(mSystemConfiguration.SpiRwProtect==1) {
    Status = EfiCreateEventReadyToBootEx (
               TPL_CALLBACK,
               SpiBiosProtectionFunction,
               NULL,
               &mReadyToBootEvent
               );
  }
  //
  // Create a ReadyToBoot Event to run the thermalzone init process
  //
  Status = EfiCreateEventReadyToBootEx (
             TPL_CALLBACK,
             InitThermalZone,
             NULL,
             &mReadyToBootEvent
             );  
 
  ReportStatusCodeEx (
    EFI_PROGRESS_CODE,
    EFI_COMPUTING_UNIT_CHIPSET | EFI_CU_PLATFORM_DXE_STEP1,
    0,
    &gEfiCallerIdGuid,
    NULL,
    NULL,
    0
    );

#if defined(SENSOR_INFO_VAR_SUPPORT) && SENSOR_INFO_VAR_SUPPORT != 0
  //
  // Initialize Sensor Info variable
  //
  InitializeSensorInfoVariable();
#endif
  InitPchPlatformPolicy(&mPlatformInfo);
  InitVlvPlatformPolicy();

  //
  //  Add usb policy
  //
  InitializeClockRouting();
  InitializeSlotInfo();
  InitTcoReset();

  //
  //Init ExI
  //
  InitExI();

  ReportStatusCodeEx (
    EFI_PROGRESS_CODE,
    EFI_COMPUTING_UNIT_CHIPSET | EFI_CU_PLATFORM_DXE_STEP2,
    0,
    &gEfiCallerIdGuid,
    NULL,
    NULL,
    0
    );

  //
  // Install PCI Bus Driver Hook
  //
  PciBusDriverHook();

  InitItk();

  ReportStatusCodeEx (
    EFI_PROGRESS_CODE,
    EFI_COMPUTING_UNIT_CHIPSET | EFI_CU_PLATFORM_DXE_STEP3,
    0,
    &gEfiCallerIdGuid,
    NULL,
    NULL,
    0
    );


  //
  // Initialize Password States and Callbacks
  //
  PchInitBeforeBoot();

#if defined SUPPORT_LVDS_DISPLAY && SUPPORT_LVDS_DISPLAY

#endif

#if defined(FIRMWARE_ID_BACKWARD_COMPATIBLE) && (FIRMWARE_ID_BACKWARD_COMPATIBLE != 0)
  //
  // Re-write Firmware ID if it is changed
  //
  InitFirmwareId();
#endif

  ReportStatusCodeEx (
    EFI_PROGRESS_CODE,
    EFI_COMPUTING_UNIT_CHIPSET | EFI_CU_PLATFORM_DXE_STEP4,
    0,
    &gEfiCallerIdGuid,
    NULL,
    NULL,
    0
    );


  Status = gBS->CreateEventEx (
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  EnableAcpiCallback,
                  NULL,
                  &gEfiEventExitBootServicesGuid,
                  &mEfiExitBootServicesEvent
                  );

  //
  // Adjust RTC deafult time to be BIOS-built time.
  //
  Status = gBS->CreateEvent (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    AdjustDefaultRtcTimeCallback,
                    NULL,
                    &RtcEvent
                    );
  if (!EFI_ERROR (Status)) {
      Status = gBS->RegisterProtocolNotify (
                      &gEfiEndOfDxeEventGroupGuid,
                      RtcEvent,
                      &RtcCallbackReg
                      );

  }

  return EFI_SUCCESS;
}

/**
  Source Or Destination with Length bytes.

  @param[in] Destination   Target memory
  @param[in] Source        Source memory
  @param[in] Length        Number of bytes

  @retval None

**/
VOID
EfiOrMem (
  IN VOID   *Destination,
  IN VOID   *Source,
  IN UINTN  Length
  )
{
  CHAR8 *Destination8;
  CHAR8 *Source8;

  if (Source < Destination) {
    Destination8  = (CHAR8 *) Destination + Length - 1;
    Source8       = (CHAR8 *) Source + Length - 1;
    while (Length--) {
      *(Destination8--) |= *(Source8--);
    }
  } else {
    Destination8  = (CHAR8 *) Destination;
    Source8       = (CHAR8 *) Source;
    while (Length--) {
      *(Destination8++) |= *(Source8++);
    }
  }
}

VOID
PchInitBeforeBoot()
{
  //
  // Saved SPI Opcode menu to fix EFI variable unable to write after S3 resume.
  //
  S3BootScriptSaveMemWrite (
                         S3BootScriptWidthUint32,
                         (UINTN)(SPI_BASE_ADDRESS + (R_PCH_SPI_OPMENU0)),
                         1,
                         (VOID *)(UINTN)(SPI_BASE_ADDRESS + (R_PCH_SPI_OPMENU0)));

  S3BootScriptSaveMemWrite (
                         S3BootScriptWidthUint32,
                         (UINTN)(SPI_BASE_ADDRESS + (R_PCH_SPI_OPMENU1)),
                         1,
                         (VOID *)(UINTN)(SPI_BASE_ADDRESS + (R_PCH_SPI_OPMENU1)));

  S3BootScriptSaveMemWrite (
                         S3BootScriptWidthUint16,
                         (UINTN)(SPI_BASE_ADDRESS + R_PCH_SPI_OPTYPE),
                         1,
                         (VOID *)(UINTN)(SPI_BASE_ADDRESS + R_PCH_SPI_OPTYPE));

  S3BootScriptSaveMemWrite (
                         S3BootScriptWidthUint16,
                         (UINTN)(SPI_BASE_ADDRESS + R_PCH_SPI_PREOP),
                         1,
                         (VOID *)(UINTN)(SPI_BASE_ADDRESS + R_PCH_SPI_PREOP));

  //
  // Saved MTPMC_1 for S3 resume.
  //
  S3BootScriptSaveMemWrite (
                         S3BootScriptWidthUint32,
                         (UINTN)(PMC_BASE_ADDRESS + R_PCH_PMC_MTPMC1),
                         1,
                         (VOID *)(UINTN)(PMC_BASE_ADDRESS + R_PCH_PMC_MTPMC1));
  return;
}

VOID
EFIAPI
ReadyToBootFunction (
  EFI_EVENT  Event,
  VOID       *Context
  )
{
  //
  // save LAN info to a variable
  //
  if (NULL != mPciLanInfo) {
    gRT->SetVariable (
           L"PciLanInfo",
           &gEfiPciLanInfoGuid,
           EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
           mPciLanCount * sizeof(PCI_LAN_INFO),
           mPciLanInfo
           );
  }

  if (NULL != mPciLanInfo) {
    gBS->FreePool (mPciLanInfo);
    mPciLanInfo = NULL;
  }
}

/**

  Initializes manufacturing and config mode setting.

**/
VOID
InitMfgAndConfigModeStateVar()
{
  EFI_PLATFORM_SETUP_ID           *BootModeBuffer;
  VOID                            *HobList;


  HobList = GetFirstGuidHob(&gEfiPlatformBootModeGuid);
  if (HobList != NULL) {
    BootModeBuffer = GET_GUID_HOB_DATA (HobList);

      //
      // Check if in Manufacturing mode
      //
      if ( !CompareMem (
              &BootModeBuffer->SetupName,
              MANUFACTURE_SETUP_NAME,
              StrSize (MANUFACTURE_SETUP_NAME)
              ) ) {
        mMfgMode = TRUE;
      }



  }

}

/**

  Initializes manufacturing and config mode setting.

**/
VOID
InitPlatformBootMode()
{
  EFI_PLATFORM_SETUP_ID           *BootModeBuffer;
  VOID                            *HobList;

  HobList = GetFirstGuidHob(&gEfiPlatformBootModeGuid);
  if (HobList != NULL) {
    BootModeBuffer = GET_GUID_HOB_DATA (HobList);
    mPlatformBootMode = BootModeBuffer->PlatformBootMode;
  }
}

/**

  Initializes ITK.

**/
VOID
InitItk(
  )
{
  EFI_STATUS                          Status;
  UINT16                              ItkModBiosState;
  UINT8                               Value;
  UINTN                               DataSize;
  UINT32                              Attributes;

  //
  // Setup local variable according to ITK variable
  //
  //
  // Read ItkBiosModVar to determine if BIOS has been modified by ITK
  // If ItkBiosModVar = 0 or if variable hasn't been initialized then BIOS has not been modified by ITK modified
  // Set local variable VAR_EQ_ITK_BIOS_MOD_DECIMAL_NAME=0 if BIOS has not been modified by ITK
  //
  DataSize = sizeof (Value);
  Status = gRT->GetVariable (
                  ITK_BIOS_MOD_VAR_NAME,
                  &gItkDataVarGuid,
                  &Attributes,
                  &DataSize,
                  &Value
                  );
  if (Status == EFI_NOT_FOUND) {
    //
    // Variable not found, hasn't been initialized, intialize to 0
    //
    Value=0x00;
  //
  // Write variable to flash.
  //
  gRT->SetVariable (
         ITK_BIOS_MOD_VAR_NAME,
         &gItkDataVarGuid,
         EFI_VARIABLE_RUNTIME_ACCESS |
         EFI_VARIABLE_NON_VOLATILE |
         EFI_VARIABLE_BOOTSERVICE_ACCESS,
         sizeof (Value),
         &Value
         );

}
  if ( (!EFI_ERROR (Status)) || (Status == EFI_NOT_FOUND) ) {
    if (Value == 0x00) {
      ItkModBiosState = 0x00;
    } else {
      ItkModBiosState = 0x01;
    }
    gRT->SetVariable (
           VAR_EQ_ITK_BIOS_MOD_DECIMAL_NAME,
           &gEfiNormalSetupGuid,
           EFI_VARIABLE_BOOTSERVICE_ACCESS,
           2,
           (void *)&ItkModBiosState
           );
  }
}

#if defined(FIRMWARE_ID_BACKWARD_COMPATIBLE) && (FIRMWARE_ID_BACKWARD_COMPATIBLE != 0)

/**

  Initializes the BIOS FIRMWARE ID from the FIRMWARE_ID build variable.

**/
STATIC
VOID
InitFirmwareId(
  )
{
  EFI_STATUS   Status;
  CHAR16       FirmwareIdNameWithPassword[] = FIRMWARE_ID_NAME_WITH_PASSWORD;

  //
  // First try writing the variable without a password in case we are
  // upgrading from a BIOS without password protection on the FirmwareId
  //
  Status = gRT->SetVariable(
                  (CHAR16 *)&gFirmwareIdName,
                  &gFirmwareIdGuid,
                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
                  EFI_VARIABLE_RUNTIME_ACCESS,
                  sizeof( FIRMWARE_ID ) - 1,
                  FIRMWARE_ID
                  );

  if (Status == EFI_INVALID_PARAMETER) {

    //
    // Since setting the firmware id without the password failed,
    // a password must be required.
    //
    Status = gRT->SetVariable(
                    (CHAR16 *)&FirmwareIdNameWithPassword,
                    &gFirmwareIdGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
                    EFI_VARIABLE_RUNTIME_ACCESS,
                    sizeof( FIRMWARE_ID ) - 1,
                    FIRMWARE_ID
                    );
  }
}
#endif

VOID
UpdateDVMTSetup(
  )
{
    //
  // Workaround to support IIA bug.
  // IIA request to change option value to 4, 5 and 7 relatively
  // instead of 1, 2, and 3 which follow Lakeport Specs.
  // Check option value, temporary hardcode GraphicsDriverMemorySize
  // Option value to fulfill IIA requirment. So that user no need to
  // load default and update setupvariable after update BIOS.
  //   Option value hardcoded as: 1 to 4, 2 to 5, 3 to 7.
  // *This is for broadwater and above product only.
  //

  SYSTEM_CONFIGURATION        SystemConfiguration;
  UINTN                       VarSize;
  EFI_STATUS                  Status;

  VarSize = sizeof(SYSTEM_CONFIGURATION);
  Status = gRT->GetVariable(
                  NORMAL_SETUP_NAME,
                  &gEfiNormalSetupGuid,
                  NULL,
                  &VarSize,
                  &SystemConfiguration
                  );

  if (EFI_ERROR (Status) || VarSize != sizeof(SYSTEM_CONFIGURATION)) {
    //The setup variable is corrupted
    VarSize = sizeof(SYSTEM_CONFIGURATION);
    Status = gRT->GetVariable(
              L"SetupRecovery",
              &gEfiNormalSetupGuid,
              NULL,
              &VarSize,
              &SystemConfiguration
              );
    ASSERT_EFI_ERROR (Status);
  }

  if((SystemConfiguration.GraphicsDriverMemorySize < 4) && !EFI_ERROR(Status) ) {
    switch (SystemConfiguration.GraphicsDriverMemorySize){
      case 1:
        SystemConfiguration.GraphicsDriverMemorySize = 4;
        break;
      case 2:
        SystemConfiguration.GraphicsDriverMemorySize = 5;
        break;
      case 3:
        SystemConfiguration.GraphicsDriverMemorySize = 7;
        break;
      default:
        break;
     }

    Status = gRT->SetVariable (
                    NORMAL_SETUP_NAME,
                    &gEfiNormalSetupGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                    sizeof(SYSTEM_CONFIGURATION),
                    &SystemConfiguration
                    );
  }
}

UINT8
ReadCmosBank1Byte (
  IN  EFI_CPU_IO2_PROTOCOL            *CpuIo,
  IN  UINT8                           Index
  )
{
  UINT8                               Data;

  CpuIo->Io.Write (CpuIo, EfiCpuIoWidthUint8, 0x72, 1, &Index);
  CpuIo->Io.Read (CpuIo, EfiCpuIoWidthUint8, 0x73, 1, &Data);
  return Data;
}

VOID
WriteCmosBank1Byte (
  IN  EFI_CPU_IO2_PROTOCOL            *CpuIo,
  IN  UINT8                           Index,
  IN  UINT8                           Data
  )
{
  CpuIo->Io.Write (
              CpuIo,
              EfiCpuIoWidthUint8,
              0x72,
              1,
              &Index
              );
  CpuIo->Io.Write (
              CpuIo,
              EfiCpuIoWidthUint8,
              0x73,
              1,
              &Data
              );
}

